#!/usr/bin/env python
#
#       MajeRe_Gui.py
#       
#       Copyright 2009 Brandon Invergo <brandon@brandoninvergo.com>
#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

import sys

try:
    import pygtk
    #tell pyGTK, if possible, that we want GTKv2
    pygtk.require("2.0")
except:
    #Some distributions come with GTK2, but not pyGTK
    pass

try:
    import gtk
    import gtk.glade
    
except:
    print "You need to install pyGTK or GTKv2 ",
    print "or set your PYTHONPATH correctly."
    print "try: export PYTHONPATH=",
    print "/usr/local/lib/python2.2/site-packages/"
    sys.exit(1)

import gtksourceview
from gtk.gdk import CONTROL_MASK, SHIFT_MASK
from gobject import TYPE_INT, TYPE_STRING
import MajeRe_Engine

class MajeRe_Gui:
    """This class constructs the MajeRe GUI"""
    script_buffer = gtksourceview.SourceBuffer()
    console_buffer = gtk.TextBuffer()
    r_eng = MajeRe_Engine.MajeRe_Engine()
    r_input_waiting = False
    open_brackets = ["{", "[", "("]
    closed_brackets = ["}", "]", ")"]
    bracket_counts = [0, 0, 0]
    command_history = [""]
    command_history_mark = -1
    clipboard = gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD)

    
    def on_console_entry(self, widget, entry):
        """This function handles commands entered in the console input"""
        entry_text = entry.get_text()
        self.do_R_command(entry_text)
        entry.set_text('')
    
    def on_script_key_event(self, widget, event):
        """This function handles key events sent from the script_buffer"""
        key = gtk.gdk.keyval_name(event.keyval)
        if event.state & CONTROL_MASK:
            # If Ctrl-Return is pressed, send a command to R and output
            # to the console
            if key == "Return":
                # If text is selected, split the selection by lines,
                # send each line individually to R, and output to the 
                # console
                if self.script_buffer.get_has_selection():
                    selection_bounds = self.script_buffer.get_selection_bounds()
                    selected_text = self.script_buffer.get_text(selection_bounds[0], \
                        selection_bounds[1])
                    selected_lines = selected_text.splitlines(False)
                    for line in selected_lines:
                        self.do_R_command(line)
                # ...otherwise, send the text in the current line of the
                # script editor
                else:
                    cursor_iter = self.script_buffer.get_iter_at_mark(self.script_buffer.get_insert())
                    cursor_line = cursor_iter.get_line()
                    line_iter = self.script_buffer.get_iter_at_line(cursor_line)
                    end_line_iter = line_iter.copy()
                    end_line_iter.forward_to_line_end()
                    script_entry = self.script_buffer.get_text(line_iter, \
                            end_line_iter)
                    self.do_R_command(script_entry)
                    if end_line_iter.forward_line() == False:
                        self.script_buffer.insert(end_line_iter,"\n")
                        end_line_iter.forward_line()
                    self.script_buffer.place_cursor(end_line_iter)
                return True
        elif event.state & SHIFT_MASK:
            if key == "braceleft":
                cursor_iter = self.script_buffer.get_iter_at_mark(self.script_buffer.get_insert())
                self.script_buffer.insert(cursor_iter,"{\n\t")
                return True
            if key == "braceright":
                cursor_iter = self.script_buffer.get_iter_at_mark(self.script_buffer.get_insert())
                new_char = cursor_iter.copy()
                new_char.backward_char()
                if new_char.get_char() == "\t":
                    self.script_buffer.delete(new_char,cursor_iter)
                    self.script_buffer.insert(cursor_iter, "}\n")
                else:
                    self.script_buffer.insert(cursor_iter, "\n}\n")
                return True
        return False
    
    def on_menu_open_script(self, widget):
        self.open_script_dialog.show()
        
    def on_open_script_open_button(self, widget):
        print "Hi\n"
        try:
            opened_script = open(self.open_script_dialog.get_filename(),"r")
            print opened_script
        except:
            print "Error opening script.\n"
        self.script_buffer.set_text(opened_script.read())
        opened_script.close()
    
    def on_open_script_cancel_button(self, widget):
        self.open_script_dialog.destroy()
        
    def on_menu_export_script(self, widget):
        self.export_script_dialog.show()
    
    def on_menu_cut(self, widget):
        self.script_buffer.cut_clipboard(self.clipboard, True)
        
    def on_menu_copy(self, widget):
        self.script_buffer.copy_clipboard(self.clipboard)
        
    def on_menu_paste(self, widget):
        self.script_buffer.paste_clipboard(self.clipboard, None, True)
        
    def on_menu_undo(self, widget):
        if self.script_buffer.can_undo():
            self.script_buffer.undo()
            
    def on_menu_redo(self, widget):
        if self.script_buffer.can_redo():
            self.script_buffer.redo()
    
    def on_console_key_event(self, widget, event):
        key = gtk.gdk.keyval_name(event.keyval)
        if key == "Up":
            self.command_history_mark = self.command_history_mark + 1
            if self.command_history_mark == len(self.command_history):
                self.command_history_mark = 0
            widget.set_text(self.command_history[self.command_history_mark])
            return True
        elif key == "Down":
            self.command_history_mark = self.command_history_mark - 1
            if self.command_history_mark < 0:
                self.command_history_mark = len(self.command_history) - 1
            widget.set_text(self.command_history[self.command_history_mark])
            return True
        return False
            
    def on_menu_undo(self, widget):
        self.script_buffer.undo()
    
    def close_application(self, widget):
        gtk.main_quit()
             
    def do_R_command(self, command):
        """This function interacts with R by sending commands to it"""
        command = command.lstrip('\t')
        
        if command == "":
            return False
        
        if self.r_input_waiting == False:
            command_output = '>' + command + '\n'
        else:
            command_output = '... ' + command + '\n'
            self.chain_command = self.chain_command + command
        
        for i in range(0,len(self.open_brackets)):
            self.bracket_counts[i] = self.bracket_counts[i] + \
                command.count(self.open_brackets[i])
        for i in range(0,len(self.closed_brackets)):
            self.bracket_counts[i] = self.bracket_counts[i] - \
                command.count(self.closed_brackets[i])
        
        if len(filter(lambda x: x > 0, self.bracket_counts)) != 0:
            if self.r_input_waiting == False:
                self.r_input_waiting = True
                self.chain_command = command
        else:
            if self.r_input_waiting == True:
                self.r_input_waiting = False
                command = self.chain_command
                self.chain_command = ""

        enditer = self.console_buffer.get_end_iter()
        self.console_buffer.insert(enditer, command_output)
        bold_begin = enditer.copy()
        bold_begin.backward_line()
        bold_end = bold_begin.copy()
        bold_end.forward_to_line_end()
        # Make the command bold (keep the results normal)
        self.console_buffer.apply_tag_by_name("bold", bold_begin, bold_end)
        if self.r_input_waiting == False:
            try:
                self.command_history.insert(0, command)
                self.command_history_mark = 0
                res = self.r_eng.R(command)
            except:
                # Update this to do proper R error handling
                res = "Syntax error.\n"
            self.console_buffer.insert(enditer, res)
        
    def init_script_buffer(self):
        """This function initializes some settings for the script text buffer"""
        lang_man = gtksourceview.SourceLanguagesManager()
        langs = lang_man.get_available_languages()
        lang_names = [x.get_name() for x in langs]
        R_lang_index = lang_names.index("R")
        if R_lang_index:
            self.script_buffer.set_language(langs[R_lang_index])
        self.script_buffer.set_check_brackets(True)
        self.script_buffer.set_highlight(True)        
        
    def __init__(self):
        """This function initializes and displays the GUI"""
        self.init_script_buffer()
        self.console_buffer.create_tag("bold", weight=700)
        gladefile="MajeRe.glade"
        
        # Load the Glade GUI
        self.glade_gui = gtk.glade.XML(gladefile)
        
        # Load the main MajeRe window
        self.main_window = self.glade_gui.get_widget("Main_window")
        
        # A GtkSourceView widget is added the hard way
        script_textview = gtksourceview.SourceView(self.script_buffer)
        script_textview.set_show_line_numbers(True)
        script_textview.set_auto_indent(True)
        script_textview.set_tabs_width(4)
        script_textview.set_smart_home_end(True)
        script_textview.connect("key_press_event", self.on_script_key_event)
        script_scroll = self.glade_gui.get_widget("Script_scrolledwindow")
        script_scroll.add_with_viewport(script_textview)
        script_textview.show()
        
        # Set up the R console
        console_output = self.glade_gui.get_widget("R_console_output")
        console_output.set_buffer(self.console_buffer)
        
        # Set up the data tables tree
        self.dt_treestore = gtk.TreeStore(TYPE_STRING, \
            TYPE_INT)
            
        ###TEMP###
        dt_test_top = self.dt_treestore.append(None, ["Test Data", 1000])
        dt_test_mid = self.dt_treestore.append(dt_test_top, ["Test Data Group 1", 500])
        dt_test_low = self.dt_treestore.append(dt_test_mid, ["Test Data Object 1", 500])
        dt_test_low = self.dt_treestore.append(dt_test_mid, ["Test Data Object 2", 500])
        dt_test_mid = self.dt_treestore.append(dt_test_top, ["Test Data Group 2", 38])
        dt_test_low = self.dt_treestore.append(dt_test_mid, ["Test Data Object 1", 38])
        dt_test_low = self.dt_treestore.append(dt_test_mid, ["Test Data Object 3", 38])
        dt_test_low = self.dt_treestore.append(dt_test_mid, ["Test Data Object 4", 38])
            
        dt_treeview = self.glade_gui.get_widget("Data_tables_tree")
        dt_treeview.set_model(self.dt_treestore)
        dt_namecol = gtk.TreeViewColumn("Name")
        dt_treeview.append_column(dt_namecol)
        dt_namecell = gtk.CellRendererText()
        dt_namecol.pack_start(dt_namecell, True)
        dt_ncol = gtk.TreeViewColumn("n")
        dt_treeview.append_column(dt_ncol)
        dt_ncell = gtk.CellRendererText()
        dt_ncol.pack_start(dt_ncell, True)
        dt_namecol.add_attribute(dt_namecell, 'text', 0)
        dt_ncol.add_attribute(dt_ncell, 'text', 1)


        
        main_callbacks = {"gtk_main_quit" : self.close_application, \
            "on_menu_open_script" : self.on_menu_open_script, \
            "on_menu_export_script" : self.on_menu_export_script, \
            "on_menu_copy" : self.on_menu_copy, \
            "on_menu_cut"  : self.on_menu_cut, \
            "on_menu_paste": self.on_menu_paste, \
            "on_menu_undo" : self.on_menu_undo, \
            "on_menu_redo" : self.on_menu_redo, \
            "on_R_console_entry_activate" : \
            (self.on_console_entry, self.glade_gui.get_widget("R_console_entry")), \
            "on_R_console_key_event" : self.on_console_key_event, \
            "on_open_script_open_button" : self.on_open_script_open_button,\
            "on_open_script_cancel_button" : self.on_open_script_cancel_button}
        self.glade_gui.signal_autoconnect(main_callbacks)

        self.export_script_dialog = self.glade_gui.get_widget("export_script_file_chooser")
        
        self.open_script_dialog = self.glade_gui.get_widget("open_script_file_chooser")
    
def main():
    """The main function"""
    # Enter the main Gtk loop
    gtk.main()
    return 0
        
if __name__ == "__main__":
    MajeRe_Gui()
    main()  
